package com.iphanta.community.tags.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iphanta.community.cache.TagCache;
import com.iphanta.community.cache.TagDimensionCache;
import com.iphanta.community.cache.TagItemCache;
import com.iphanta.community.common.BusinessException;
import com.iphanta.community.common.utils.ResultUtils;
import com.iphanta.community.tags.entity.Tag;
import com.iphanta.community.tags.entity.TagItem;
import com.iphanta.community.tags.mapper.TagItemMapper;
import com.iphanta.community.tags.mapper.TagsMapper;
import com.iphanta.community.tags.service.TagsService;
import com.iphanta.community.tags.traffic.*;
import com.iphanta.community.tags.traffic.vo.SimpleTagNode;
import com.iphanta.community.tags.traffic.vo.TagInfo;
import com.iphanta.community.tags.traffic.vo.TagNode;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 墨声 E-mail: zehong.hongframe.huang@gmail.com
 * create time: 3/7/22 5:17 PM
 */
@Service
public class TagsServiceImpl implements TagsService {

    @Autowired
    private TagsMapper tagsMapper;

    @Autowired
    private TagCache tagCache;

    @Autowired
    private TagItemMapper tagItemMapper;

    @Autowired
    private TagItemCache tagItemCache;

    @Autowired
    private TagDimensionCache tagDimensionCache;

    @Override
    public int add(AddTagItemRequest request) {
        TagItem current = this.tagItemMapper.selectOne(request.getCode());
        if (Objects.nonNull(current)) {
            throw new BusinessException("标签已存在.", ResultUtils.FAIL_ON_BUSINESS.result());
        }
        int currentLevel = 1;
        if (Objects.nonNull(request.getParentCode())) {
            TagItem parent = this.tagItemMapper.selectOne(request.getParentCode());
            if (Objects.equals(parent.getLevel(), 3)) {
                throw new BusinessException("3级标签无法添加子节点.", ResultUtils.FAIL_ON_BUSINESS.result());
            }
            currentLevel = parent.getLevel() + 1;
        }

        TagItem item = new TagItem();
        BeanUtils.copyProperties(request, item);
        item.setLevel(currentLevel);
        return this.tagItemMapper.insert(item);
    }

    @Override
    public ModifyTagItemResponse modify(ModifyTagItemRequest request) {
        TagItem item = this.tagItemMapper.selectOne(request.getCode());
        if (Objects.isNull(item)) {
            throw new BusinessException("查无数据.", ResultUtils.FAIL_ON_BUSINESS.result());
        }
        item.setName(request.getName());
        item.setDimensionCode(request.getDimensionCode());
        item.setType(request.getType());
        item.setVisible(request.getVisible());
        item.setSort(request.getSort());
        this.tagItemMapper.modify(item);
        ModifyTagItemResponse response = new ModifyTagItemResponse();
        response.setSuccess(true);
        this.tagItemCache.refresh(item.getCode());
        return response;
    }

    @Override
    public TagDimensionResponse tagDimension() {
        TagDimensionResponse response = new TagDimensionResponse();
        response.setDimensions(this.tagItemMapper.selectTagDimension());
        return response;
    }

    @Override
    public QueryTagItemResponse query(QueryTagItemRequest request) {
        String queryKey = "%%";
        if (!StringUtils.isBlank(request.getKeyword())) {
            queryKey = "%" + request.getKeyword() + "%";
        }
        PageHelper.startPage(request.getPageNum(), request.getPageSize());
        List<TagItem> items = this.tagItemMapper.select(queryKey);
        PageInfo<TagItem> pageInfo = new PageInfo<>(items);

        List<TagNode> nodes = new ArrayList<>();
        for (TagItem item : items) {
            TagNode node = new TagNode();
            BeanUtils.copyProperties(item, node);
            node.setDimensionDesc(this.tagDimensionCache.getDesc(node.getDimensionCode()));
            nodes.add(node);
        }

        QueryTagItemResponse response = new QueryTagItemResponse();
        response.setList(nodes);
        response.setTotal(pageInfo.getTotal());
        response.setLastPage(pageInfo.getLastPage());
        response.setPageSize(pageInfo.getPageSize());
        response.setPageNum(pageInfo.getPageNum());
        return response;
    }

    @Override
    public AllTagItemResponse queryAll() {
        List<TagItem> items = this.tagItemMapper.select("%%");
        List<SimpleTagNode> list = new ArrayList<>();
        items.forEach(e -> {
            if (e.getLevel() < 3) {
                SimpleTagNode node = new SimpleTagNode();
                node.setCode(e.getCode());
                node.setName(e.getName());
                node.setLevel(e.getLevel());
                list.add(node);
            }
        });
        AllTagItemResponse response = new AllTagItemResponse();
        response.setList(list);
        return response;
    }

    @Override
    public QueryOneTagItemResponse queryOne(QueryOneTagItemRequest request) {
        TagItem item = this.tagItemMapper.selectOne(request.getCode());
        if (Objects.isNull(item)) {
            throw new BusinessException("查无数据.", ResultUtils.FAIL_ON_BUSINESS.result());
        }

        TagNode node = new TagNode();
        BeanUtils.copyProperties(item, node);
        node.setDimensionDesc(this.tagDimensionCache.getDesc(node.getDimensionCode()));
        if (Objects.equals(item.getLevel(), 1)) {
            List<TagItem> level2 = childes(request.getCode());
            if (!CollectionUtils.isEmpty(level2)) {
                List<TagNode> childes = new ArrayList<>();
                for (TagItem item2 : level2) {
                    TagNode child = new TagNode();
                    BeanUtils.copyProperties(item2, child);
                    child.setDimensionDesc(this.tagDimensionCache.getDesc(child.getDimensionCode()));
                    childes.add(child);
                    List<TagItem> level3 = childes(item2.getCode());
                    if (!CollectionUtils.isEmpty(level3)) {
                        List<TagNode> grandchildes = new ArrayList<>();
                        for (TagItem item3 : level3) {
                            TagNode grandchild = new TagNode();
                            BeanUtils.copyProperties(item3, grandchild);
                            grandchild.setDimensionDesc(this.tagDimensionCache.getDesc(grandchild.getDimensionCode()));
                            grandchildes.add(grandchild);
                        }
                        child.setChildes(grandchildes);
                    }
                }
                node.setChildes(childes);
            }
        } else if (Objects.equals(item.getLevel(), 2)) {
            TagItem item1 = this.tagItemMapper.selectOne(item.getParentCode());
            TagNode parent = new TagNode();
            BeanUtils.copyProperties(item1, parent);
            parent.setDimensionDesc(this.tagDimensionCache.getDesc(parent.getDimensionCode()));
            parent.setChildes(Arrays.asList(node));
            List<TagItem> level3 = childes(item.getCode());
            if (!CollectionUtils.isEmpty(level3)) {
                List<TagNode> childes = new ArrayList<>();
                level3.forEach(e -> {
                    TagNode child = new TagNode();
                    BeanUtils.copyProperties(e, child);
                    child.setDimensionDesc(this.tagDimensionCache.getDesc(child.getDimensionCode()));
                    childes.add(child);
                });
                node.setChildes(childes);
            }
            node = parent;
        } else {
            TagItem level2 = this.tagItemMapper.selectOne(item.getParentCode());
            TagNode parent = new TagNode();
            BeanUtils.copyProperties(level2, parent);
            parent.setDimensionDesc(this.tagDimensionCache.getDesc(parent.getDimensionCode()));
            parent.setChildes(Arrays.asList(node));
//            node.setParent(parent);
            TagItem level1 = this.tagItemMapper.selectOne(level2.getParentCode());
            TagNode grandparent = new TagNode();
            BeanUtils.copyProperties(level1, grandparent);
            grandparent.setDimensionDesc(this.tagDimensionCache.getDesc(grandparent.getDimensionCode()));
            grandparent.setChildes(Arrays.asList(parent));
//            parent.setParent(grandparent);
            node = grandparent;
        }
        QueryOneTagItemResponse response = new QueryOneTagItemResponse();
        response.setNode(node);
        return response;
    }

    private List<TagItem> childes(String parentCode) {
        return this.tagItemMapper.selectByParent(parentCode);
    }

    @Override
    public TagsResponse list(TagsV1Request request) {

        List<Tag> tags = this.tagsMapper.selectByType(request.getType());
        List<TagInfo> list = new ArrayList<>();
        for (Tag tag : tags) {
            TagInfo item = new TagInfo();
            BeanUtils.copyProperties(tag, item);
            list.add(item);
        }
        TagsResponse response = new TagsResponse();
        response.setTags(list);
        return response;
    }

    @Override
    public TagsResponse tags(TagsRequest request) {
        Integer type = request.getType();
        List<TagItem> items;
        List<TagInfo> tags;
        if(Objects.isNull(type)) {
            items = this.tagItemCache.allType();
        } else {
            items = this.tagItemCache.type(type);
        }
        if(CollectionUtils.isEmpty(items)) {
            tags = Collections.EMPTY_LIST;
        } else {
            Stream<TagItem> stream = items.stream();
            if(Objects.nonNull(request.getParent())) {
                stream = stream.filter(e -> Objects.equals(e.getParentCode(), request.getParent()));
            }
            if(Objects.isNull(request.getParent()) && Objects.nonNull(request.getLevel())) {
                stream = stream.filter(e -> Objects.equals(e.getLevel(), request.getLevel()));
            }
            if(Objects.nonNull(request.getVisible())) {
                stream = stream.filter(e -> Objects.equals(e.getVisible(), request.getVisible()));
            }
            tags = stream.map(e -> {
                TagInfo tag = new TagInfo();
                tag.setCode(e.getCode());
                tag.setName(e.getName());
                tag.setType(e.getType());
                tag.setSort(e.getSort());
                tag.setVisible(e.getVisible());
                return tag;
            }).collect(Collectors.toList());
        }

        TagsResponse response = new TagsResponse();
        response.setTags(tags);
        return response;
    }

    @Override
    public List<TagInfo> tags(List<String> codes) {
        if (CollectionUtils.isEmpty(codes)) {
            return null;
        }
        List<TagInfo> tags = new ArrayList<>();
        for (String item : codes) {
            TagItem tagItem = this.tagItemCache.get(item);
            if (Objects.isNull(tagItem)) {
                continue;
            }
            TagInfo tagInfo = new TagInfo();
            tagInfo.setCode(tagItem.getCode());
            tagInfo.setName(tagItem.getName());
            tagInfo.setSort(tagItem.getSort());
            tagInfo.setType(tagItem.getType());
            tagInfo.setVisible(tagItem.getVisible());
            tags.add(tagInfo);
        }
        tags.sort((o1, o2) -> o1.getSort() - o2.getSort());
        return tags;
    }

    @Override
    public List<TagInfo> tags(String codes) {
        if (StringUtils.isBlank(codes)) {
            return Collections.EMPTY_LIST;
        }
        String[] arr = codes.split("\\|");
        if (ArrayUtils.isEmpty(arr)) {
            return Collections.EMPTY_LIST;
        }

        return tags(Arrays.asList(arr));
    }

    @Override
    public List<String> sortTagCodes(List<String> codes) {
        List<TagInfo> tagInfos = tags(codes);
        return tagInfos.stream().map(e -> e.getCode()).collect(Collectors.toList());
    }

    @Override
    public Boolean validCode(String code) {
        TagItem item = this.tagItemCache.get(code);
        if(Objects.isNull(item)) {
            throw new BusinessException("标签[" + code + "]不存在", ResultUtils.FAIL_ON_BUSINESS.result());
        }
        return true;
    }

    @Override
    public Boolean validCode(List<String> codes) {
        for(String code : codes) {
            validCode(code);
        }
        return true;
    }

    @Override
    public Boolean validCode(String[] codes) {
        for(String code : codes) {
            validCode(code);
        }
        return true;
    }

}
